Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | /** * Translation Service * Centralized translation management with i18n support */ const fs = require('fs'); const path = require('path'); const { pool } = require('../config/database'); class TranslationService { constructor() { this.translations = {}; this.defaultLanguage = 'en'; this.loadTranslations(); this.refreshDefaultLanguage(); } /** * Load all translation files */ loadTranslations() { const localesPath = path.join(__dirname, '../locales'); try { const files = fs.readdirSync(localesPath); files.forEach(file => { if (file.endsWith('.json')) { const lang = file.replace('.json', ''); const filePath = path.join(localesPath, file); this.translations[lang] = JSON.parse(fs.readFileSync(filePath, 'utf8')); } }); } catch (error) { console.error('Error loading translations:', error.message); } } async refreshDefaultLanguage() { try { const [rows] = await pool.execute( `SELECT setting_value FROM system_settings_kv WHERE setting_key = 'default_language' LIMIT 1` ); if (rows && rows[0] && rows[0].setting_value) { this.defaultLanguage = rows[0].setting_value; } } catch (error) { console.error('Error loading default language:', error.message); } } setDefaultLanguage(language) { if (language && typeof language === 'string') { this.defaultLanguage = language; } } /** * Get translation for a key * @param {string} key - Translation key (e.g., 'errors.not_found') * @param {string} lang - Language code (default: 'en') * @param {object} params - Parameters for interpolation * @returns {string} Translated text */ t(key, lang = 'en', params = {}) { const keys = key.split('.'); let translation = this.translations[lang] || this.translations[this.defaultLanguage]; for (const k of keys) { if (translation && translation[k]) { translation = translation[k]; } else { // Fallback to default language translation = this.translations[this.defaultLanguage]; for (const k2 of keys) { if (translation && translation[k2]) { translation = translation[k2]; } else { return key; // Return key if translation not found } } break; } } // Interpolate parameters if (typeof translation === 'string' && params) { Object.keys(params).forEach(param => { translation = translation.replace(new RegExp(`{{${param}}}`, 'g'), params[param]); }); } return translation; } /** * Get user's preferred language from request * @param {object} req - Express request object * @returns {string} Language code */ getLanguage(req) { // Priority: 1. User preference, 2. Accept-Language header, 3. Default if (req.user && req.user.preferred_language) { return req.user.preferred_language; } if (req.headers['accept-language']) { const lang = req.headers['accept-language'].split(',')[0].split('-')[0]; if (this.translations[lang]) { return lang; } } return this.defaultLanguage; } /** * Middleware to add translation function to request */ middleware() { return (req, res, next) => { const lang = this.getLanguage(req); req.t = (key, params) => this.t(key, lang, params); req.lang = lang; next(); }; } } // Export singleton instance module.exports = new TranslationService(); |